package cn.jianyongjie.basic.service.impl;

import cn.jianyongjie.basic.constant.BaseConstant;
import cn.jianyongjie.basic.jwt.LoginSuccessObj;
import cn.jianyongjie.basic.dto.LoginDto;
import cn.jianyongjie.basic.dto.WechatLogin;
import cn.jianyongjie.basic.exception.CustomException;
import cn.jianyongjie.basic.jwt.JwtUtils;
import cn.jianyongjie.basic.jwt.RsaUtils;
import cn.jianyongjie.basic.service.ILoginService;
import cn.jianyongjie.basic.utils.AjaxResult;
import cn.jianyongjie.basic.utils.HttpClientUtils;
import cn.jianyongjie.basic.utils.MD5Utils;
import cn.jianyongjie.basic.utils.StrUtils;
import cn.jianyongjie.system.domain.Menu;
import cn.jianyongjie.system.mapper.MenuMapper;
import cn.jianyongjie.system.mapper.PermissionMapper;
import cn.jianyongjie.user.domain.Logininfo;
import cn.jianyongjie.user.domain.User;
import cn.jianyongjie.user.domain.Wxuser;
import cn.jianyongjie.user.mapper.LogininfoMapper;
import cn.jianyongjie.user.mapper.UserMapper;
import cn.jianyongjie.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
* @Description: TODO
* @Author: JianYongJie
* @Version: 1.0
* @Date:  2022/5/10 11:04
*/
@Service
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxuserMapper wxuserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private MenuMapper menuMapper;

    /*
    * @Title: LoginServiceImpl
    * @Description: 登录
    * @Author: JianYongJie
    * @Version: 1.0
    * @Date:  2022/5/10 16:34
    * @Parameters: * @param dto
    * @Return java.util.HashMap<java.lang.String,java.lang.Object>
    */
    @Override
    public HashMap<String, Object> administrators(LoginDto dto) {
        // 抽出重复代码
        String password = dto.getPassword();
        Integer type = dto.getType();
        String username = dto.getUsername();

        // 判断是否为空
        if (StringUtils.isEmpty(password)||StringUtils.isEmpty(username)||StringUtils.isEmpty(type)){
            throw new CustomException("账号或者密码错误,请重新输入");
        }

        // 根据账号查询数据
        Logininfo logininfo = logininfoMapper.LoadByDto(username,type);

        // 判断是否查询到了数据
        if (logininfo == null){
            throw new CustomException("账号错误请重新输入");
        }

        // 如果查询到了数据就判断密码是否一致
        // 将查询到的数据的盐值取出
        String salt = logininfo.getSalt();
        //2 通过这个盐值加密前端传递的密码
        String md5Pwd = MD5Utils.encrypByMd5(password+salt);

        // 判断密码是否一致
        if (!logininfo.getPassword().equals(md5Pwd)){
            throw new CustomException("密码错误请重新输入");
        }


//        // 设置一个UUID
//        String token = UUID.randomUUID().toString();
//        // 将数据保存到redis,设置30分钟有效时间
//        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
//
//        // 为了安全将用户的盐值和密码清空
//        logininfo.setPassword(null);
//        logininfo.setSalt(null);
//
//        // 创建一个map
//        HashMap<String, Object> map = new HashMap<>();
//
//        // 将数据装进map里面
//        map.put("token",token);
//        map.put("logininfo",logininfo);

        // 返回查询到的信息
        return LoginStorageJWT(logininfo);


    }

    /*
    * @Title: LoginServiceImpl
    * @Description: 登录菜单按钮权限存储jwt
    * @Author: JianYongJie
    * @Version: 1.0
    * @Date:  2022/5/16 14:36
    * @Parameters: * @param logininfo
    * @Return void
    */
    private HashMap<String, Object> LoginStorageJWT(Logininfo logininfo) {

        // 将查询到的权限封装到类里面
        LoginSuccessObj loginSuccessObj = new LoginSuccessObj();
        loginSuccessObj.setLogininfo(logininfo);

        if (logininfo.getType()==0){
            // 查询员工的按钮权限
            List<String> s = permissionMapper.loadLogininfoById(logininfo.getId());
            // 查询员工的菜单权限
            List<Menu> menu = menuMapper.loadLogininfoByid(logininfo.getId());
            // 进行封装
            loginSuccessObj.setMenus(menu);
            loginSuccessObj.setPermissions(s);
        }

        try {
            // 使用jwt的私钥进行加密
            PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource("hrm_auth_rsa").getFile());
            // 进行加密，设置30分钟有效时间
            String token = JwtUtils.generateTokenExpireInMinutes(loginSuccessObj, privateKey, 30);

            // 创建一个map
            HashMap<String, Object> map = new HashMap<>();

            // 将盐值和密码设置为null
            logininfo.setPassword(null);
            logininfo.setSalt(null);

            // 将数据装进map里面
            map.put("token",token);
            map.put("logininfo",logininfo);
            map.put("menus", loginSuccessObj.getMenus());
            map.put("permissions", loginSuccessObj.getPermissions());
            // 返回数据
            return map;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /*
    * @Title: LoginServiceImpl
    * @Description: 微信扫码登录
    * @Author: JianYongJie
    * @Version: 1.0
    * @Date:  2022/5/12 15:00
    * @Parameters: * @param code
    * @Return java.util.HashMap<java.lang.String,java.lang.Object>
    */
    @Override
    public AjaxResult wechat(WechatLogin code) {

        String code1 = code.getCode();

        // 判断是否为空
        if (StringUtils.isEmpty(code)){
            throw new CustomException("授权码为空，请重试");
        }

        // 将APPID、SECRET和code一起拼接
        String codeURL =
                BaseConstant.WECHAT_LOGIN_CODE
                        // 将字符串里面的数据，换成全局常量里面设置的数据
                        .replace("APPID",BaseConstant.APPID)
                        .replace("SECRET",BaseConstant.SECRET)
                        .replace("CODE",code1);

        // 使用APPID、SECRET、code发起请求得到token
        String token = HttpClientUtils.httpGet(codeURL);
        // 将json格式转为对象
        JSONObject jsonObject = JSONObject.parseObject(token);
        // 根据名称获取字符串数据
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");

        // 根据openid查询是否已经有了该用户
        Wxuser wxuser = wxuserMapper.LoadByOpenid(openid);

        // 判断是否有数据
        if (!StringUtils.isEmpty(wxuser) && !StringUtils.isEmpty(wxuser.getUserId())){
            // 如果不等于null，就直接免密登录
            // 根据查询到的数据的userId查询数据
            Logininfo logininfo = logininfoMapper.LoadByUserId(wxuser.getUserId());

            // 将数据封装到jwt里面
            HashMap<String, Object> map = LoginStorageJWT(logininfo);
            return AjaxResult.success().setResultObj(map);
//            // 将数据保存到redis,设置30分钟有效时间
//            redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
//
//            // 创建一个map
//            HashMap<String, Object> map = new HashMap<>();
//
//            // 将数据装进map里面
//            map.put("token",token);
//            map.put("logininfo",logininfo);
//
//            // 返回查询到的信息
//            return AjaxResult.success().setResultObj(map);
        }

        // 如果为null就跳转注册页面
        // 将注册页面的相关数据带给前端

        String binder = "?accessToken="+access_token+"&openId="+openid;
        // 创建一个map
        HashMap<String, Object> map = new HashMap<>();
        // 将数据装进去
        map.put("binder",binder);

        System.out.println(AjaxResult.error().setResultObj(map));

        // 返回给前端
        return AjaxResult.error().setResultObj(map);

    }


    /*
    * @Title: LoginServiceImpl
    * @Description: 手机授权注册
    * @Author: JianYongJie
    * @Version: 1.0
    * @Date:  2022/5/12 21:18
    * @Parameters: * @param code
    * @Return java.util.HashMap<java.lang.String,java.lang.Object>
    */
    @Override
    public HashMap<String, Object> binder(WechatLogin code) {
        // 抽出重复代码
        String phone = code.getPhone();
        String openId = code.getOpenId();
        String accessToken = code.getAccessToken();
        String verifyCode = code.getVerifyCode();

        // 空校验
        if (StringUtils.isEmpty(phone)){
            throw new CustomException("电话不能为空");
        }

        // 校验手机号
        boolean check = check(phone);
        if (check==false){
            throw new CustomException("电话只能为数字");
        }

        if (StringUtils.isEmpty(openId)){
            throw new CustomException("openId不能为空");
        }

        if (StringUtils.isEmpty(accessToken)){
            throw new CustomException("accessToken不能为空");
        }

        if (StringUtils.isEmpty(verifyCode)){
            throw new CustomException("请填写验证码");
        }

        // 使用全局常量类拼接电话得到拼接后的参数
        String businessKey = BaseConstant.REGISTER_BINDER_CODE+ phone;
        // 使用拼接后的参数查询redis里面的短信验证码
        Object smsCode = redisTemplate.opsForValue().get(businessKey);
        // 将时间戳去掉，进行比较
        String sms = smsCode.toString().split(":")[0];
        // 判断验证码是否一致
        if (smsCode==null){
            // 如果等于null，就代表短信验证码已经过期了
            throw new CustomException("短信验证码已经过期，请重新获取");
        }else {
            // 如果不等于null，就判断用户输入的验证码是否一致
            if (!verifyCode.equalsIgnoreCase(sms)){
                throw new CustomException("短信验证码不一致请重新输入");
            }
        }

        // 查询手机号是否已经注册了
        Logininfo logininfo = logininfoMapper.LoadByDto(phone, 1);

        // 这个字符串是用来接收查询到的数据的
        User user = null;

        if (!StringUtils.isEmpty(logininfo)){
            // 注册了就根据logininfo的id查询用户
            user = userMapper.LoadByLogininfo(logininfo.getId());
        }else {
            // 没有就注册
            // 保存t_logininfo和t_user表
            user = dtoUser(phone);
            // 将user的数据拷贝给logininfo
            logininfo = dtoLogininfo(user);

            // 进行新增,但先新增t_logininfo
            logininfoMapper.save(logininfo);
            // 将新增的t_logininfo的id设置给t_user表
            user.setLogininfoId(logininfo.getId());
            // 进行新增用户
            userMapper.save(user);
        }

        // 拼接前端需要的链接
        String url =
                BaseConstant.GET_WX_URL
                        .replace("ACCESS_TOKEN",accessToken)
                        .replace("OPENID",openId);

        // 发起链接，获取json数据
        String wx = HttpClientUtils.httpGet(url);

        // 将json数据转为对象
        Wxuser wxuser = JSONObject.parseObject(wx, Wxuser.class);

        // 将user的id设置给wxuser
        wxuser.setUserId(user.getId());

        // 新增Wxuser
        wxuserMapper.save(wxuser);

        // 最后进行登录
        return LoginStorageJWT(logininfo);
//        // 设置一个UUID
//        String token = UUID.randomUUID().toString();
//        // 将数据保存到redis,设置30分钟有效时间
//        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
//
//        // 创建一个map
//        HashMap<String, Object> map = new HashMap<>();
//
//        // 为了安全将用户的盐值和密码清空
//        logininfo.setPassword(null);
//        logininfo.setSalt(null);
//
//        // 将数据装进map里面
//        map.put("token",token);
//        map.put("logininfo",logininfo);
//
//        // 返回查询到的信息
//        return map;

    }

    private Logininfo dtoLogininfo(User dtoUser) {

        Logininfo logininfo = new Logininfo();
        // 拷贝user设置的参数
        BeanUtils.copyProperties(dtoUser,logininfo);
        // 设置type
        logininfo.setType(1);
        return logininfo;
    }

    private User dtoUser(String phone) {
        User user = new User();
        // 将名称设置为电话
        user.setUsername(phone);
        // 设置电话
        user.setPhone(phone);
        // 设置时间
        user.setCreatetime(new Date());
        // 设置盐值
        String salt = StrUtils.getComplexRandomString(32);
        //2 通过这个盐值加密密码
        // 设置一个默认密码
        String password = "123";
        String md5Pwd = MD5Utils.encrypByMd5(password+salt);
        // 保存盐值
        user.setSalt(salt);
        // 保存被盐值加密的密码
        user.setPassword(md5Pwd);
        return user;
    }

    //验证是否为手机号(手机)
    boolean check(String ds) {
        if (ds.matches("^1[3|4|5|6|7|8|9][0-9]\\d{8}$")) {
            return true;
        } else {
            return false;

        }
    }
}
